#include "ScanLineRasterizerMT.h"
#include <QThread>
#include <QMutex>
#include <QWaitCondition>

void ScanLineRasterizerMT_VP_Thread::init(int s, int e, const std::vector<Face>* vb, VERTEXSHADER shader, ShaderGlobals* glob){
	start_idx = s;
	end_idx = e;

	face_buffer = vb;

	mShaderGlobals = glob;

	mVertexShader = shader;
	mVShaderParams.globals = glob;
	mVShaderParams.material = glob->material;

	renderTargetWidth = mShaderGlobals->renderTarget->depthBuffer->width;
	renderTargetHeight = mShaderGlobals->renderTarget->depthBuffer->height;

	wmi1 = renderTargetWidth-1;
	hmi1 = renderTargetHeight-1;

	mCondition.wakeAll();
}

void ScanLineRasterizerMT_VP_Thread::run()
{
	while(mAlive){
		QMutexLocker lock(&mMutex);
		mCondition.wait(&mMutex);
		if (!mAlive)
			break;
		for (int i = start_idx; i < end_idx; i++){
			transformTriangle(i);
		}
		mWorkingThreads->deref();
	}
}


void ScanLineRasterizerMT_VP_Thread::transformTriangle(int face_idx)
{
	const Face& face = (*face_buffer)[face_idx];
	TransformedFace &tface = (transformed_face_buffer)[face_idx];
	tface.numVertices = 3;

	static Real cp = 0.03125;//near clip plane??
	TransformedVertex *vertices = tface.vertices;
	TransformedVertex *newVertices = tface.newVertices;
	
	

	for (int i = 0; i < 3; i++)
	{
		mVShaderParams.in = &face.v[i];
		mVShaderParams.out = &vertices[i];
		mVertexShader(&mVShaderParams);
	}

	//Skip triangles in front of the z clip plane and outside the camera frustum
	if (vertices[0].p.w >= cp && vertices[1].p.w >= cp && vertices[2].p.w >= cp){
		for (int j = 0; j < 3; j++){
			Real invW = 1.0f / vertices[j].p.w;
			ndc[j].x = vertices[j].p.x * invW;
			ndc[j].y = vertices[j].p.y * invW;
			ndc[j].z = vertices[j].p.z * invW;
		}
		if(ndc[0].x < -1.0 && ndc[1].x < -1.0 && ndc[2].x < -1.0) return;
		if(ndc[0].x >  1.0 && ndc[1].x >  1.0 && ndc[2].x >  1.0) return;
		if(ndc[0].y < -1.0 && ndc[1].y < -1.0 && ndc[2].y < -1.0) return;
		if(ndc[0].y >  1.0 && ndc[1].y >  1.0 && ndc[2].y >  1.0) return;
		if(ndc[0].z < -1.0 && ndc[1].z < -1.0 && ndc[2].z < -1.0) return;
		if(ndc[0].z >  1.0 && ndc[1].z >  1.0 && ndc[2].z >  1.0) return;
	}

	// find 1 vertex or 2 vertices behind the z clip plane (cp must be close to 0.0 but greater than 0.0)
	int vtcc = 0, vtc, vntc; // vertices to clip count

	for(int j = 0; j < 3; j++)
	{
		if(vertices[j].p.w < cp)
		{
			vtcc++;
			vtc = j; // vertex behind the z clip plane
		}
		else
		{
			vntc = j; // vertex in front of the z clip plane
		}
	}

	if(vtcc == 3) return;

	Real factor1, factor2;
	int v1, v2;
	

	// clip the 2 vertices behind the z clip plane
	if(vtcc == 2)
	{
		v1 = (vntc + 1) % 3;

		v1diff.x = vertices[vntc].p.x - vertices[v1].p.x;
		v1diff.y = vertices[vntc].p.y - vertices[v1].p.y;
		v1diff.z = vertices[vntc].p.z - vertices[v1].p.z;
		v1diff.w = vertices[vntc].p.w - vertices[v1].p.w;

		factor1 = (cp - vertices[v1].p.w) / v1diff.w;

		vertices[v1].p.x = v1diff.x * factor1 + vertices[v1].p.x;
		vertices[v1].p.y = v1diff.y * factor1 + vertices[v1].p.y;
		vertices[v1].p.z = v1diff.z * factor1 + vertices[v1].p.z;
		vertices[v1].p.w = v1diff.w * factor1 + vertices[v1].p.w;

		for(int j = 0; j < MAX_VARYINGS; j++)
		{
			varying1diff[j] = vertices[vntc].varyings[j] - vertices[v1].varyings[j];
			vertices[v1].varyings[j] = varying1diff[j] * factor1 + vertices[v1].varyings[j];
		}

		v2 = (vntc + 2) % 3;

		v2diff.x = vertices[vntc].p.x - vertices[v2].p.x;
		v2diff.y = vertices[vntc].p.y - vertices[v2].p.y;
		v2diff.z = vertices[vntc].p.z - vertices[v2].p.z;
		v2diff.w = vertices[vntc].p.w - vertices[v2].p.w;

		factor2 = (cp - vertices[v2].p.w) / v2diff.w;

		vertices[v2].p.x = v2diff.x * factor2 + vertices[v2].p.x;
		vertices[v2].p.y = v2diff.y * factor2 + vertices[v2].p.y;
		vertices[v2].p.z = v2diff.z * factor2 + vertices[v2].p.z;
		vertices[v2].p.w = v2diff.w * factor2 + vertices[v2].p.w;

		for(int j = 0; j < MAX_VARYINGS; j++)
		{
			varying2diff[j] = vertices[vntc].varyings[j] - vertices[v2].varyings[j];
			vertices[v2].varyings[j] = varying2diff[j] * factor2 + vertices[v2].varyings[j];
		}
	}



	// clip the 1 vertex behind the z clip plane
	if(vtcc == 1)
	{
		for(int j = 0; j < 3; j++)
		{
			newVertices[j].p.x = vertices[j].p.x;
			newVertices[j].p.y = vertices[j].p.y;
			newVertices[j].p.z = vertices[j].p.z;
			newVertices[j].p.w = vertices[j].p.w;

			for(int k = 0; k < MAX_VARYINGS; k++)
			{
				newVertices[j].varyings[k] = vertices[j].varyings[k];
			}
		}

		v2 = (vtc + 2) % 3;

		v2diff.x = vertices[v2].p.x - vertices[vtc].p.x;
		v2diff.y = vertices[v2].p.y - vertices[vtc].p.y;
		v2diff.z = vertices[v2].p.z - vertices[vtc].p.z;
		v2diff.w = vertices[v2].p.w - vertices[vtc].p.w;

		factor2 = (cp - vertices[vtc].p.w) / v2diff.w;

		newVertices[v2].p.x = vertices[vtc].p.x = v2diff.x * factor2 + vertices[vtc].p.x;
		newVertices[v2].p.y = vertices[vtc].p.y = v2diff.y * factor2 + vertices[vtc].p.y;
		newVertices[v2].p.z = vertices[vtc].p.z = v2diff.z * factor2 + vertices[vtc].p.z;
		newVertices[v2].p.w = vertices[vtc].p.w = v2diff.w * factor2 + vertices[vtc].p.w;

		for(int j = 0; j < MAX_VARYINGS; j++)
		{
			varying2diff[j] = vertices[v2].varyings[j] - vertices[vtc].varyings[j];
			newVertices[v2].varyings[j] = vertices[vtc].varyings[j] = varying2diff[j] * factor2 + vertices[vtc].varyings[j];
		}

		v1 = (vtc + 1) % 3;

		v1diff.x = newVertices[v1].p.x - newVertices[vtc].p.x;
		v1diff.y = newVertices[v1].p.y - newVertices[vtc].p.y;
		v1diff.z = newVertices[v1].p.z - newVertices[vtc].p.z;
		v1diff.w = newVertices[v1].p.w - newVertices[vtc].p.w;

		factor1 = (cp - newVertices[vtc].p.w) / v1diff.w;

		newVertices[vtc].p.x = v1diff.x * factor1 + newVertices[vtc].p.x;
		newVertices[vtc].p.y = v1diff.y * factor1 + newVertices[vtc].p.y;
		newVertices[vtc].p.z = v1diff.z * factor1 + newVertices[vtc].p.z;
		newVertices[vtc].p.w = v1diff.w * factor1 + newVertices[vtc].p.w;

		for(int j = 0; j < MAX_VARYINGS; j++)
		{
			varying1diff[j] = newVertices[v1].varyings[j] - newVertices[vtc].varyings[j];
			newVertices[vtc].varyings[j] = varying1diff[j] * factor1 + newVertices[vtc].varyings[j];
		}
		tface.numVertices = 6;
		rasterizeTriangle(newVertices, &tface.newEdges, tface.newBackface, tface.newFragment);
	}
	rasterizeTriangle(vertices, &tface.edges, tface.backface, tface.fragments);
} 



inline void setEdge(Fragment* f1, Fragment* f2, Edge* e)
{
	if (f1->fragCoord.y <= f2->fragCoord.y){
		e->f1 = f1;
		e->f2 = f2;
	}else{
		e->f1 = f2;
		e->f2 = f1;
	}
	//e->diff = e->f2->fragCoord - e->f1->fragCoord;
	e->diff.x = e->f2->fragCoord.x - e->f1->fragCoord.x;
	e->diff.y = e->f2->fragCoord.y - e->f1->fragCoord.y;
	e->diff.z = e->f2->fragCoord.z - e->f1->fragCoord.z;
	e->diff.w = e->f2->fragCoord.w - e->f1->fragCoord.w;
	for (int i = 0; i < MAX_VARYINGS; i++)
	{
		e->varyingsdiff[i] = e->f2->varyings[i] - e->f1->varyings[i];
	}
}

void ScanLineRasterizerMT_VP_Thread::rasterizeTriangle(TransformedVertex* vertices, EdgeData* edges, Real& a, Fragment* fragment)
{
	
	
	Real invW[3];
	//project vertices into screen space
	for (int i = 0; i < 3; i++){
		invW[i] = 1.0 / vertices[i].p.w;
		fragment[i].fragCoord.x = (vertices[i].p.x * invW[i] * 0.5 + 0.5) * wmi1;
		fragment[i].fragCoord.y = (vertices[i].p.y * invW[i] * 0.5 + 0.5) * hmi1;
	}

	//cull back-face triangles
	a = 0.0;
	for ( int i = 0; i < 3; i++){
		int mod = (i+1); //(i+1)%3
		if (mod >= 3)
			mod -= 3;
		//int mod = (i+1)%3;
		a += fragment[i].fragCoord.x * fragment[mod].fragCoord.y - fragment[mod].fragCoord.x * fragment[i].fragCoord.y;
	}
	a *= 0.5;

	//if ( face->backface < 0.0) 
	//	return ;//for counter clockwise orientation, for clockwise the sign of a should be reversed

	//copy attributes
	for (int i = 0; i < 3; i++){
		fragment[i].fragCoord.z = vertices[i].p.z * invW[i];
		fragment[i].fragCoord.w = invW[i];
		for (int j = 0; j < MAX_VARYINGS; j++){
			fragment[i].varyings[j] = vertices[i].varyings[j] * invW[i];
		}
	}


	// rasterize triangle with perspective correct z and attribute values interpolation
	// http://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf
	// http://joshbeam.com/articles/triangle_rasterization/

	//Edge edge[3];
	Real maxLength = 0;
	edges->longEdge = 0;
	for (int i = 0; i < 3; i++)
	{
		int mod = (i+1); //(i+1)%3
		if (mod >= 3)
			mod -= 3;
		//int mod = (i+1)%3;
		setEdge(&fragment[i], &fragment[mod], &edges->edges[i]);
		if (edges->edges[i].diff.y > maxLength){
			maxLength = edges->edges[i].diff.y;
			edges->longEdge = i;
		}
	}
	edges->shortEdge1 = (edges->longEdge + 1);
	if (edges->shortEdge1 >= 3)
		edges->shortEdge1-=3;
	edges->shortEdge2 = (edges->longEdge + 2);
	if (edges->shortEdge2 >= 3)
		edges->shortEdge2-=3;
}